package top.dopen.home.work.common;

import com.qq.weixin.mp.aes.AesException;
import com.qq.weixin.mp.aes.WXBizMsgCrypt;
import lombok.extern.slf4j.Slf4j;
import org.dom4j.Document;
import org.dom4j.DocumentHelper;
import org.dom4j.Element;
import org.dom4j.io.OutputFormat;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.annotation.PostConstruct;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import java.util.TreeMap;
import java.util.UUID;

/**
 * <p>
 *	微信消息签名认证工具
 * </p>
 *
 * @Auther Dopen
 * @Date 2020/8/4
 */
@Slf4j
@Component
public class WechatValidate {
	@Value("${wechat.message.token}")
	private String sToken;
	@Value("${wechat.corpid}")
	private String sCorpID;
	@Value("${wechat.message.encodingAESKey}")
	private String sEncodingAESKey;

	private WXBizMsgCrypt wxcpt ;
	@PostConstruct
	public void init(){
		try {
			wxcpt = new WXBizMsgCrypt(sToken, sEncodingAESKey, sCorpID);
		} catch (AesException e) {
			log.warn("消息验证器初始化失败");
		}
	}

	/**
	 * 验证回调URL
	 * @param sign
	 * @param timestamp
	 * @param nonce
	 * @param echostr
	 * @return 明文消息
	 */
	public String validateUrl(String sign,String timestamp,String nonce,String echostr) {
		String sEchoStr = null;
		try {
			sEchoStr = wxcpt.VerifyURL(sign, timestamp,
					nonce, echostr);
		} catch (AesException e) {
			log.warn("验证签名失败",e);
		}
		return sEchoStr;
	}

	/**
	 *对用户回复的消息解密
	 * @param sign
	 * @param timestamp
	 * @param nonce
	 * @param msgEntity
	 * @return
	 */
	public Map<String, String> decryptBody(String sign, String timestamp, String nonce, String msgEntity) {
		TreeMap<String, String> rec = null;
		try {
			String sEchoStr = wxcpt.DecryptMsg(sign, timestamp,
					nonce, msgEntity);
			rec = parseXml2Map(sEchoStr);
		} catch (AesException e) {
			log.warn("验证签名失败",e);
		}
		return rec;
	}


	/**
	 *企业回复用户消息的加密
	 * @param timestamp
	 * @param nonce
	 * @param msgEntity
	 * @return
	 */
	public String encryptBody(String timestamp, String nonce, String msgEntity) {
		String rec = null;
		try {
			rec = wxcpt.EncryptMsg(msgEntity, timestamp,
					nonce);
		} catch (AesException e) {
			log.warn("加密失败",e);
		}
		return rec;
	}

	/**
	 * 将XML字符串转为TreeMap
	 * @param xml
	 * @return
	 */
	public static TreeMap<String, String> parseXml2Map(String xml) {
		TreeMap data = new TreeMap();

		try {
			DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
			DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
			InputStream stream = new ByteArrayInputStream(xml.getBytes("UTF-8"));
			org.w3c.dom.Document doc = documentBuilder.parse(stream);
			doc.getDocumentElement().normalize();
			NodeList nodeList = doc.getDocumentElement().getChildNodes();

			for(int idx = 0; idx < nodeList.getLength(); ++idx) {
				Node node = nodeList.item(idx);
				if (node.getNodeType() == 1) {
					org.w3c.dom.Element element = (org.w3c.dom.Element)node;
					data.put(element.getNodeName(), element.getTextContent());
				}
			}
			try {
				stream.close();
			} catch (Exception e) {
				e.printStackTrace();
			}
		} catch (IOException | ParserConfigurationException | SAXException e) {
			e.printStackTrace();
		}

		return data;
	}


	/**
	 * 根据响应内容进行加密签名，生成XML
	 * @param content 未加密消息XML
	 * @return 加密签名后通用消息类型
	 */
	public String messageSign(String content) {
		String timestamp = String.valueOf(System.currentTimeMillis()),
				nonce = UUID.randomUUID().toString().replace("-","");

		return encryptBody(timestamp,nonce,content);
	}
	/**
	 * 将map转换为XML字符串<br/>
	 * 1. key为节点名称，value为节点文字
	 * 2. 根节点为xml，无编码头
	 * @param map
	 * @return
	 */
	@Deprecated
	public static String  map2XmlString (Map<String,String> map){
		Document document = DocumentHelper.createDocument();
		Element root = document.addElement("xml");
		for(Map.Entry<String,String> item:map.entrySet()){
			Element element = root.addElement(item.getKey());
			element.setText(item.getValue());
		}
		OutputFormat format = OutputFormat.createPrettyPrint();
		format.setEncoding("utf-8");
		return document.getRootElement().asXML();
	}
}
